package com.by.basicgateway.intercept.filter;

import com.by.toolkit.consts.WebGlobalConst;
import com.by.toolkit.utils.ByIfTrueUtil;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Component
public class AddProjectCodeGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {

    @Override
    public GatewayFilter apply(Object config) {
        return new AddProjectCodeGateWayFilter();
    }

    static class AddProjectCodeGateWayFilter implements GatewayFilter {

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            String requestUrl = request.getPath().toString();
            String requestMethod = request.getMethod().name();
            if (HttpMethod.POST.toString().equals(requestMethod) || HttpMethod.PUT.toString().equals(requestMethod)) {
                return DataBufferUtils.join(exchange.getRequest().getBody())
                        .flatMap(dataBuffer -> {
                            byte[] bytes = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(bytes);
                            String postRequestBody = new String(bytes, StandardCharsets.UTF_8);
                            log.info("请求url:{}， 请求类型：{}， 请求参数：{}", requestUrl, requestMethod, postRequestBody);

                            Map<String, String> paramsMap = decodeBody(postRequestBody);
                            log.info("[AddProjectCodeGateWayFilter][filter] paramsMap = {}", paramsMap);

                            exchange.getAttributes().put("POST_BODY", postRequestBody);
                            DataBufferUtils.release(dataBuffer);
                            Flux<DataBuffer> cachedFlux = Flux.defer(() -> Mono.just(exchange.getResponse().bufferFactory().wrap(bytes)));
                            // 下面的将请求体再次封装写回到request里，传到下一级，否则，由于请求体已被消费，后续的服务将取不到值
                            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                                @Override
                                public Flux<DataBuffer> getBody() {
                                    return cachedFlux;
                                }
                            };

                            String projectCode = paramsMap.get(WebGlobalConst.PROJECT_CODE);
                            log.info("[AddProjectCodeGateWayFilter][filter] projectCode = {}", projectCode);
                            if (StringUtils.isNotBlank(projectCode)) {
                                mutatedRequest = mutatedRequest.mutate()
                                        .headers((httpHeaders) -> httpHeaders.add(WebGlobalConst.PROJECT_CODE, projectCode))
                                        .build();
                            }
                            String first = mutatedRequest.getHeaders().getFirst(WebGlobalConst.PROJECT_CODE);
                            log.info("[AddProjectCodeGateWayFilter][filter] headerCode = {}", first);
                            return chain.filter(exchange.mutate().request(mutatedRequest).build());
                        });
            } else if (HttpMethod.GET.toString().equals(requestMethod) || HttpMethod.DELETE.toString().equals(requestMethod)) {
                MultiValueMap<String, String> queryParams = request.getQueryParams();
                log.info("[AddProjectCodeGateWayFilter][filter] requestMethod = {}, queryParams = {}", requestMethod, queryParams);
                String projectCode = queryParams.getFirst(WebGlobalConst.PROJECT_CODE);
                if (StringUtils.isNotBlank(projectCode)) {
                    request = request.mutate()
                            .headers((httpHeaders) -> httpHeaders.add(WebGlobalConst.PROJECT_CODE, projectCode))
                            .build();
                }
                return chain.filter(exchange.mutate().request(request).build());
            }
            return chain.filter(exchange);
        }

        private Map<String, String> decodeBody(String body) {
            return ByIfTrueUtil.getIfTrueOrElse(StringUtils.isNotBlank(body)
                    , () -> Arrays.stream(body.split("&"))
                            .map(s -> s.split("="))
                            .collect(Collectors.toMap(arr -> arr[0], arr -> arr[1]))
                    , () -> Maps.newHashMap());
        }
    }

}
